/*****************************************************************************
 *  ENTROPY - emerging network to reduce orwellian potency yield
 *
 *  Copyright (C) 2002 Juergen Buchmueller <pullmoll@stop1984.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software Foundation,
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 *	$Id: osd.c,v 1.7 2005/07/12 23:12:29 pullmoll Exp $
 *****************************************************************************/
#include "osd.h"
#include "shmalloc.h"
#include "config.h"
#include "tools.h"
#include "logger.h"
#if STORE_TYPE == 2  /* MySQL store */
#include "store.h"  /* for store_disconnect_db() */
#endif

#if	!HAVE_UTIMES
#include <utime.h>
#endif

#if	HAVE_MMAP == 0
int proc_key = -1;
int proc_shmid = -1;
#endif
proc_list_t *g_proc = NULL;

#if		USE_SVID_SEMAPHORES || USE_FLOCK_SEMAPHORES
osd_sem_global_t *semglobals = NULL;
#endif
#if		USE_FLOCK_SEMAPHORES
int sem_fd = -1;
#endif

int verbose = 0;

int info(const char *fmt, ...)
{
	va_list arg;
	int length;

	if (0 == verbose) {
		return 0;
	}
	va_start(arg, fmt);
	length = vfprintf(stdout, fmt, arg);
	va_end(arg);
	fflush(stdout);

	return length;
}

int die(int rc, const char *fmt, ...)
{
	va_list arg;
	int length;

	va_start(arg, fmt);
	length = vfprintf(stderr, fmt, arg);
	va_end(arg);
	fflush(stderr);

	/* send the leader of this process group a SIGHUP */
	kill(getpgrp(), SIGHUP);
	exit(rc);
}

void osd_usleep(int64_t usecs)
{
	int secs = (int)(usecs / 1000000);
	FUN("osd_usleep");

	if (usecs <= 0)
		return;
	if (secs > 0) {
		LOG(L_DEBUG,("sleep(%d)\n", secs));
		osd_sleep(secs);
		usecs -= (int64_t)1000000 * secs;
	}
	if (usecs > 0) {
#ifdef	__CYGWIN__
		if (usecs <= 500)
			usecs = 501;
#endif
		LOG(L_DEBUG,("usleep(%lld)\n", usecs));
		usleep(usecs);
	}
}

/* close all but 'keepfd' file descriptors */
void closeall(int fd, int keepfd)
{
	int fdlimit = sysconf(_SC_OPEN_MAX);
	while (fd < fdlimit) {
		if (fd != keepfd)
			close(fd);
		fd++;
	}
}

#define	MY_SEM_MAGIC	0x71734c4a
#define	SHUTDOWN()	(NULL != g_proc && -1 == g_proc->pgrp)

#if	DEBUG_SEMAPHORES
static const char *at_file_line(const char *file, unsigned line)
{
	static char buff[128];
	pm_snprintf(buff, sizeof(buff), " at %s.%u", file, line);
	return buff;
}
#else
#define	at_file_line(x)	""
#endif

#if	USE_POSIX_SEMAPHORES

/*
 * Despite the POSIX semaphore implementation's man page on FreeBSD telling
 * me that pshared does not work with sem_init(), I use them shared amongst
 * processes. I hope that I can do that because all semaphores are in shared
 * memory (either mmap()ed or SysV SHM). Let's see if it works.
 */

int _osd_sem_init(osd_sem_t *sem, int pshared, int value SEM_FILELINEARGS)
{
	sem_t *s = &sem->posix;
	int rc;
	FUN("osd_sem_init");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC == sem->magic) {
		LOG(L_ERROR,("sem %p already initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		sem->pid = 0;
		/* Destroy and initialize semaphore again */
		sem_destroy(s);
		sem_init(s, 0, value);
		return 0;
	}
	sem->pid = 0;
	rc = sem_init(s, pshared, value);
	if (0 == rc)
		sem->magic = MY_SEM_MAGIC;
	return rc;
}

int _osd_sem_trywait(osd_sem_t *sem SEM_FILELINEARGS)
{
	pid_t pid = getpid();
	sem_t *s = &sem->posix;
	int rc;
	FUN("osd_sem_trywait");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* try to lock a semaphore that already belongs to us? */
	if (pid == sem->pid) {
		LOG(L_ERROR,("sem %p already belongs to us%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		return 0;
	}
	for (;;) {
		rc = sem_trywait(s);
		if (0 == rc) {
			sem->pid = pid;
			break;
		}
		if (EINTR != errno)
			break;
	}
	return rc;
}

int _osd_sem_wait(osd_sem_t *sem SEM_FILELINEARGS)
{
	pid_t pid = getpid();
	sem_t *s = &sem->posix;
	int rc;
	FUN("osd_sem_wait");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* try to lock a semaphore that already belongs to us? */
	if (pid == sem->pid) {
		LOG(L_ERROR,("sem %p already belongs to us%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		return 0;
	}
	for (;;) {
		rc = sem_wait(s);
		if (0 == rc) {
			sem->pid = pid;
			break;
		}
		if (EINTR != errno)
			break;
	}
	return rc;
}

int _osd_sem_post(osd_sem_t *sem SEM_FILELINEARGS)
{
	pid_t pid = getpid();
	sem_t *s = &sem->posix;
	int rc;
	FUN("osd_sem_post");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* try to unlock a semaphore that no longer belongs to us? */
	if (pid != sem->pid && 0 != sem->pid) {
		LOG(L_ERROR,("sem %p belongs to PID {%d}, not us%s\n",
			sem, sem->pid, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* set pid to zero first... */
	sem->pid = 0;
	/* ...then post the semaphore */
	rc = sem_post(s);
	return rc;
}

int _osd_sem_destroy(osd_sem_t *sem SEM_FILELINEARGS)
{
	void *s = &sem->posix;
	int rc;
	FUN("osd_sem_destroy");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	sem->magic = 0;
	rc = sem_destroy(s);
	sem->pid = 0;
	return rc;
}

#elif	USE_SVID_SEMAPHORES

/* Required to be application-declared by SVID. */
union osd_semun {
	int val;
	struct semid_ds *buf;
	unsigned short *array;
} arg;

int osd_sem_global_init(osd_sem_global_t *sg)
{
	int semid;
	FUN("osd_sem_global_init");

	if ((semid = semget(IPC_PRIVATE, OSD_SEM_NSEMS, 0600)) < 0) {
		LOG(L_ERROR,("semget(IPC_PRIVATE, %d, 06000) failed (%s)\n",
			OSD_SEM_NSEMS, strerror(errno)));
		return -1;
	}

	sg->semid = semid;
	sg->nsems = OSD_SEM_NSEMS;
	sg->nextsem = 0;

	semglobals = sg;

	return 0;
}

void osd_sem_global_destroy(osd_sem_global_t *sg)
{
	FUN("osd_sem_global_destroy");

	semctl(sg->semid, 0, IPC_RMID);

	sg->semid = 0;
	semglobals = NULL;
}

int _osd_sem_init(osd_sem_t *sem, int pshared, int value SEM_FILELINEARGS)
{
	int semnum;
	union osd_semun s;
	int rc;
	FUN("osd_sem_init");

	/* we don't use pshared */
	(void)pshared;
	/* zero semun */
	memset(&s, 0, sizeof(s));

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (NULL == semglobals) {
		semglobals = &g_shm->semglobals;
		LOG(L_ERROR,("set semglobals %p%s\n",
			semglobals, at_file_line(SEM_FILELINEVARS)));
	}
	if (MY_SEM_MAGIC == sem->magic) {
		LOG(L_ERROR,("sem %p already initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		sem->pid = 0;
		s.val = value;
		semctl(sem->semid, sem->semnum, SETVAL, s);
		return 0;
	}

	semnum = semglobals->nextsem++;

	if (semnum >= semglobals->nsems) {
		LOG(L_ERROR,("ran out of semaphores, %d max%s\n",
			semglobals->nsems,
			at_file_line(SEM_FILELINEVARS)));
		errno = ENOSPC;
		return -1;
	}
	sem->pid = 0;
	s.val = value;
	rc = semctl(semglobals->semid, semnum, SETVAL, s);
	if (0 == rc) {
		sem->semid = semglobals->semid;
		sem->semnum = semnum;
		sem->magic = MY_SEM_MAGIC;
	}
	return rc;
}

int _osd_sem_trywait(osd_sem_t *sem SEM_FILELINEARGS)
{
	pid_t pid = getpid();
	struct sembuf sb;
	int rc;
	FUN("osd_sem_trywait");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* try to lock a semaphore that already belongs to us? */
	if (pid == sem->pid) {
		LOG(L_ERROR,("sem %p already belongs to us%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		return 0;
	}
	memset(&sb, 0, sizeof(sb));
	sb.sem_num = sem->semnum;
	sb.sem_op = -1;
	sb.sem_flg = SEM_UNDO|IPC_NOWAIT;
	for (;;) {
		rc = semop(sem->semid, &sb, 1);
		if (0 == rc) {
			sem->pid = pid;
			break;
		}
		if (EINTR != errno)
			break;
	}
	return rc;
}

int _osd_sem_wait(osd_sem_t *sem SEM_FILELINEARGS)
{
	pid_t pid = getpid();
	struct sembuf sb;
	int rc;
	FUN("osd_sem_wait");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* try to lock a semaphore that already belongs to us? */
	if (pid == sem->pid) {
		LOG(L_ERROR,("sem %p already belongs to us%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		return 0;
	}
	memset(&sb, 0, sizeof(sb));
	sb.sem_num = sem->semnum;
	sb.sem_op = -1;
	sb.sem_flg = SEM_UNDO;
	for (;;) {
		rc = semop(sem->semid, &sb, 1);
		if (0 == rc) {
			sem->pid = pid;
			break;
		}
		if (EINTR != errno)
			break;
	}
	return rc;
}

int _osd_sem_post(osd_sem_t *sem SEM_FILELINEARGS)
{
	pid_t pid = getpid();
	struct sembuf sb;
	int rc;
	FUN("osd_sem_post");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL\n"));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* try to unlock a semaphore that no longer belongs to us? */
	if (pid != sem->pid && 0 != sem->pid) {
		LOG(L_ERROR,("sem %p belongs to PID {%d}, not us%s\n",
			sem, sem->pid, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* set pid to zero first... */
	sem->pid = 0;
	/* ...then post the semaphore */
	memset(&sb, 0, sizeof(sb));
	sb.sem_num = sem->semnum;
	sb.sem_op = 1;
	sb.sem_flg = SEM_UNDO;
	for (;;) {
		rc = semop(sem->semid, &sb, 1);
		if (0 == rc)
			break;
		if (EINTR != errno)
			break;
	}
	return rc;
}

int _osd_sem_destroy(osd_sem_t *sem SEM_FILELINEARGS)
{
	int rc;
	FUN("osd_sem_destroy");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	sem->magic = 0;
	rc = 0; /* We pretend to destroy, but not really.  XXX */
	sem->pid = 0;
	return rc;
}

#elif	USE_FLOCK_SEMAPHORES

int osd_sem_global_init(osd_sem_global_t *sg)
{
	uint8_t *zeroes = NULL;
	size_t semsize = getpagesize();
	int i;
	int rc = -1;
	FUN("osd_sem_global_init");

	zeroes = xcalloc(1, semsize);

	pm_snprintf(sg->filename, sizeof(sg->filename), "%s%s.sem",
		g_conf->runpath, g_conf->progname);
	sg->nsems = OSD_SEM_NSEMS;
	sg->nextsem = 0;

	/* create nsems file containing nsems * semsize zeroes */
	sem_fd = open(sg->filename, O_CREAT|O_TRUNC|O_RDWR|O_BINARY, 0600);
	if (-1 == sem_fd) {
		LOG(L_ERROR,("open(%s,O_CREAT...,0600) failed (%s)\n",
			sg->filename, strerror(errno)));
		goto bailout;
	}
	for (i = 0; i < OSD_SEM_NSEMS; i++) {
		if (semsize != (size_t)write(sem_fd, zeroes, semsize)) {
			LOG(L_ERROR,("write(%s,...,%u) failed (%s)\n",
				sg->filename, (unsigned)semsize,
				strerror(errno)));
			close(sem_fd);
			sem_fd = -1;
			goto bailout;
		}
	}

	semglobals = sg;
	rc = 0;

bailout:
	xfree(zeroes);
	return rc;
}

void osd_sem_global_destroy(osd_sem_global_t *sg)
{
	FUN("osd_sem_global_destroy");

	/* close semaphore file */
	if (-1 != sem_fd) {
		close(sem_fd);
		sem_fd = -1;
	}

	/* unlink semaphore file */
	if ('\0' != sg->filename[0]) {
		LOG(L_MINOR,("unlink(%s) semaphore file\n",
			sg->filename));
		unlink(sg->filename);
		sg->filename[0] = '\0';
	}

	semglobals = NULL;
}

int _osd_sem_init(osd_sem_t *sem, int pshared, int value SEM_FILELINEARGS)
{
	size_t semsize = getpagesize();
	struct flock fl;
	int semnum;
	int rc;
	FUN("osd_sem_init");

	/* we don't use pshared */
	(void)pshared;

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	semnum = semglobals->nextsem;

	if (semnum >= semglobals->nsems) {
		LOG(L_ERROR,("ran out of semaphores, %d max%s\n",
			semglobals->nsems,
			at_file_line(SEM_FILELINEVARS)));
		errno = ENOSPC;
		return -1;
	}
	semglobals->nextsem++;
	sem->pid = 0;
	if (0 != value) {
		/* init unlocked */
		sem->semnum = semnum;
		sem->magic = MY_SEM_MAGIC;
		rc = 0;
	} else {
		/* init locked */
		memset(&fl, 0, sizeof(fl));
		fl.l_start = semnum * semsize;
		fl.l_len = semsize;
		fl.l_type = 0 == value ? F_WRLCK : F_UNLCK;
		fl.l_whence = SEEK_SET;
		for (;;) {
			rc = fcntl(sem_fd, F_SETLK, &fl);
			if (0 == rc) {
				sem->semnum = semnum;
				sem->magic = MY_SEM_MAGIC;
				break;
			}
			if (EINTR != errno)
				break;
		}
		if (0 != rc) {
			LOG(L_ERROR,("fcntl(%d,%02x) failed (%s)%s\n",
				sem_fd, semnum,
				strerror(errno),
				at_file_line(SEM_FILELINEVARS)));
		}
	}
	return rc;
}

int _osd_sem_trywait(osd_sem_t *sem SEM_FILELINEARGS)
{
	size_t semsize = getpagesize();
	pid_t pid = getpid();
	struct flock fl;
	int rc;
	FUN("osd_sem_trywait");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* try to lock a semaphore that already belongs to us? */
	if (pid == sem->pid) {
		LOG(L_ERROR,("sem %p already belongs to us%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		return 0;
	}
	memset(&fl, 0, sizeof(fl));
	fl.l_start = sem->semnum * semsize;
	fl.l_len = semsize;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;
	for (;;) {
		rc = fcntl(sem_fd, F_SETLK, &fl);
		if (0 == rc) {
			sem->pid = pid;
			break;
		}
		if (EINTR != errno)
			break;
	}
	if (0 != rc) {
		LOG(L_ERROR,("fcntl(%d,%02x) failed (%s)%s\n",
			sem_fd, sem->semnum,
			strerror(errno),
			at_file_line(SEM_FILELINEVARS)));
	}
	return rc;
}

int _osd_sem_wait(osd_sem_t *sem SEM_FILELINEARGS)
{
	size_t semsize = getpagesize();
	pid_t pid = getpid();
	struct flock fl;
	int rc;
	FUN("osd_sem_wait");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* try to lock a semaphore that already belongs to us? */
	if (pid == sem->pid) {
		LOG(L_ERROR,("sem %p already belongs to us%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		return 0;
	}
	memset(&fl, 0, sizeof(fl));
	fl.l_start = sem->semnum * semsize;
	fl.l_len = semsize;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;
	for (;;) {
		rc = fcntl(sem_fd, F_SETLKW, &fl);
		if (0 == rc) {
			sem->pid = pid;
			break;
		}
		if (EINTR != errno)
			break;
	}
	if (0 != rc) {
		LOG(L_ERROR,("fcntl(%d,%02x) failed (%s)%s\n",
			sem_fd, sem->semnum,
			strerror(errno),
			at_file_line(SEM_FILELINEVARS)));
	}
	return rc;
}

int _osd_sem_post(osd_sem_t *sem SEM_FILELINEARGS)
{
	size_t semsize = getpagesize();
	pid_t pid = getpid();
	struct flock fl;
	int rc;
	FUN("osd_sem_post");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL\n"));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* try to unlock a semaphore that no longer belongs to us? */
	if (pid != sem->pid && 0 != sem->pid) {
		LOG(L_ERROR,("sem %p belongs to PID {%d}, not us%s\n",
			sem, (int)sem->pid, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* post the semaphore */
	memset(&fl, 0, sizeof(fl));
	fl.l_start = sem->semnum * semsize;
	fl.l_len = semsize;
	fl.l_type = F_UNLCK;
	fl.l_whence = SEEK_SET;
	for (;;) {
		rc = fcntl(sem_fd, F_SETLKW, &fl);
		if (0 == rc) {
			sem->pid = 0;
			break;
		}
		if (EINTR != errno)
			break;
	}
	if (0 != rc) {
		LOG(L_ERROR,("fcntl(%d,%02x) failed (%s)%s\n",
			sem_fd, sem->semnum,
			strerror(errno),
			at_file_line(SEM_FILELINEVARS)));
	}
	return rc;
}

int _osd_sem_destroy(osd_sem_t *sem SEM_FILELINEARGS)
{
	int rc;
	FUN("osd_sem_destroy");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	sem->magic = 0;
	rc = 0; /* We pretend to destroy, but not really.  XXX */
	sem->pid = 0;
	return rc;
}

#else

#if defined(X86)
static inline int __my_sem_lock(osd_sem_t *sem)
{
	int	rc = 0;
	__asm__ __volatile__("\n"
"	movl	$1, %%eax\n"
"	movl	$0, %%edx\n"
"	cmpxchgl %%edx, %1\n"
"	jne		2f\n"
"	decl	%%eax\n"
"2:	movl	%%eax, %0"
	: "=g" (rc), "=m" (sem->value)
	: "r" (rc), "m" (sem->value)
	: "cc", "eax", "edx", "memory");
	return rc;
}
#elif defined(PPC)
static inline int __my_sem_lock(osd_sem_t *sem)
{
	int rc, tmp;
	__asm__ __volatile__("\n"
"1:	lwarx	%1,0,%2\n"
"	addic	%1,%1,-1\n"
"	bne-	2f\n"
"	stwcx.	%1,0,%2\n"
"	bne-	1b\n"
"	li		%0,0\n"
"2:\n"
	: "=r" (rc), "=&r" (tmp)
	: "r" (&sem->value), "0" (1)
	: "cr0", "memory");
	return rc;
}
#else
/*
 * This will fail many times; you should code up a secure
 * semaphore lock down in your machine's assembly code.
 */
#warning "Need an atomic assembly sempahore lock for your CPU platform!!!"
static inline int __my_sem_lock(osd_sem_t *sem)
{
	int rc = (sem->value -= 1);
	if (0 == rc)
		return 0;
	sem->value = 1;
	return 1;
}
#endif

int _osd_sem_init(osd_sem_t *sem, int pshared, int value SEM_FILELINEARGS)
{
	FUN("osd_sem_init");

	(void)pshared;

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC == sem->magic) {
		LOG(L_ERROR,("sem %p already initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		sem->pid = 0;
		sem->value = value;
		return 0;
	}
	sem->pid = 0;
	sem->magic = MY_SEM_MAGIC;
	sem->value = value;

	return 0;
}

int _osd_sem_trywait(osd_sem_t *sem SEM_FILELINEARGS)
{
	pid_t pid = getpid();
	FUN("osd_sem_trywait");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p is not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}

	/* try to lock a semaphore that already belongs to us? */
	if (pid == sem->pid && 0 == sem->value) {
		LOG(L_ERROR,("sem %p already belongs to us%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		return 0;
	}

	if (0 == __my_sem_lock(sem)) {
		sem->pid = pid;
		return 0;
	}

	if (0 != sem->pid && 0 != kill(sem->pid, 0)) {
#if	DEBUG
		pid_t old_pid = sem->pid;
#endif
		/* force value to 0 after a process holding this lock died */
		sem->value = 0;
		/* assign this semaphore to our PID */
		sem->pid = pid;
		LOG(L_ERROR,("proc {%d} is dead%s\n", old_pid,
			at_file_line(SEM_FILELINEVARS)));
		return 0;
	}

	LOG(L_ERROR,("sem %p locked by PID {%d}%s\n",
		sem, sem->pid,
		at_file_line(SEM_FILELINEVARS)));

	errno = EAGAIN;
	return -1;
}

int _osd_sem_wait(osd_sem_t *sem SEM_FILELINEARGS)
{
	pid_t pid = getpid();
	size_t delay;
	FUN("osd_sem_wait");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL\n"));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}

	/* try to lock a semaphore that already belongs to us? */
	if (pid == sem->pid && 0 == sem->value) {
		LOG(L_ERROR,("sem %p already belongs to us%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		return 0;
	}

	delay = 500;
	for (;;) {
		/* shutting down? */
		if (SHUTDOWN()) {
			return 0;
		}

		if (MY_SEM_MAGIC != sem->magic) {
			LOG(L_ERROR,("sem %p not initialized%s\n",
				sem, at_file_line(SEM_FILELINEVARS)));
			errno = EINVAL;
			return -1;
		}

		if (0 == __my_sem_lock(sem)) {
			sem->pid = pid;
			return 0;
		}

		osd_usleep(delay);
		if (delay < 1000000) {
			delay *= 2;
		} else {
			LOG(L_ERROR,("sem %p locked by PID {%d}%s\n",
				sem, sem->pid,
				at_file_line(SEM_FILELINEVARS)));
		}

		if (0 != sem->pid && 0 != kill(sem->pid, 0)) {
#if	DEBUG
			pid_t old_pid = sem->pid;
#endif
			/* force value to 0 after a process holding this lock died */
			sem->value = 0;
			/* assign this semaphore to our PID */
			sem->pid = pid;
			LOG(L_ERROR,("proc {%d} is dead%s\n",
				old_pid,
				at_file_line(SEM_FILELINEVARS)));
			return 0;
		}
	}
}

int _osd_sem_post(osd_sem_t *sem SEM_FILELINEARGS)
{
	pid_t pid = getpid();
	FUN("osd_sem_post");

	if (NULL == sem) {
		LOG(L_ERROR,("sem is NULL%s\n",
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}
	/* shutting down? */
	if (SHUTDOWN()) {
		return 0;
	}
	if (MY_SEM_MAGIC != sem->magic) {
		LOG(L_ERROR,("sem %p not initialized%s\n",
			sem, at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}

	/* try to lock a semaphore that already belongs to us? */
	if (pid != sem->pid && 0 != sem->pid) {
		LOG(L_ERROR,("sem %p belongs to PID {%d}, not us%s\n",
			sem, sem->pid,
			at_file_line(SEM_FILELINEVARS)));
		errno = EINVAL;
		return -1;
	}

	sem->pid = 0;
	sem->value = 1;
	return 0;
}

int _osd_sem_destroy(osd_sem_t *sem SEM_FILELINEARGS)
{
	pid_t pid = getpid();
	FUN("osd_sem_destroy");

	if (NULL == sem) {
		errno = EINVAL;
		return -1;
	}

	if (MY_SEM_MAGIC != sem->magic) {
		errno = EINVAL;
		return -1;
	}

	if (pid != sem->pid) {
		LOG(L_ERROR,("sem %p has PID {%d}%s\n",
			sem, sem->pid,
			at_file_line(SEM_FILELINEVARS)));
	}

	sem->magic = 0;
	sem->pid = 0;

	return 0;
}

#endif

int fork2(const char *name, int niceness, int keepfd)
{
	pid_t pid;
	int rc, status;
	FUN("fork2");

#if STORE_TYPE == 2  /* MySQL store */
	/* We shut down the db connection so it doesn't get cloned to the child.
	 * Each process should have its own database connection.
	 * (Shutting down a parent's db connection in a child causes problems.) */
	store_disconnect_db();
#endif

	if (0 == (pid = fork())) {
		/* child of first fork() */
		switch (fork()) {
		case -1:
			_exit(errno);
		case 0:
			/* child of second fork() */
			closeall(0, keepfd);
			open("/dev/null", O_RDONLY);	/* stdin */
			open("/dev/null", O_WRONLY);	/* stdout */
			open("/dev/null", O_WRONLY);	/* stderr */
#if	USE_FLOCK_SEMAPHORES
			sem_fd = open(semglobals->filename, O_RDWR|O_BINARY);
#endif
			osd_proc_add(getpid(), name);
#if	HAVE_SETPROCTITLE
			setproctitle("%s", name);
#endif
			if (niceness > 0) {
				nice(niceness);
			}
			/* return 0 to caller */
			return 0;
		default:
			/* parent of 2nd fork() just exits */
			_exit(0);
		}
	}
	/* parent of first fork */

	if (pid < 0) {
		/* first fork() failed */
		return -1;
	}

	/* wait for child to exit after 2nd fork */
	if ((rc = waitpid(pid, &status, 0)) < 0) {
		return -1;
	}

	if (WIFEXITED(status)) {
		if (0 == WEXITSTATUS(status)) {
			/* we have no meaningful pid, return 1 */
			return 1;
		} else {
			/* errno from failed 2nd fork() */
			errno = WEXITSTATUS(status);
		}
	} else {
		/* child died in some other way */
		errno = EINTR;
	}
	return -1;
}

void osd_exit(int rc)
{
	pid_t pid = getpid();
	FUN("osd_exit");
	LOG(L_MINOR,("***** EXIT {%d} '%s' rc:%d\n",
		(int)pid, proc_name(pid), rc));
	osd_proc_del(pid);
	_exit(rc);
}

#if	HAVE_MMAP
int proc_init(int argc, char **argv)
{
	char *filename = calloc(MAXPATHLEN, 1);
	uint8_t *zeroes = NULL;
	int flags, seconds, pid_fd = -1, rc = 0;
	FUN("proc_init");

	(void)argc;
	(void)argv;

	if (NULL == filename) {
		LOG(L_ERROR,("calloc(%d,%d) call failed (%s)\n",
			MAXPATHLEN, 1, strerror(errno)));
		rc = -1;
		goto bailout;
	}

	pm_snprintf(filename, MAXPATHLEN, "%s%s.pid",
		g_conf->runpath, g_conf->progname);
	pid_fd = open(filename, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600);
	if (-1 == pid_fd) {
		LOG(L_ERROR,("open('%s', O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600) call failed (%s)\n",
			filename, strerror(errno)));
		rc = -1;
		goto bailout;
	}
	zeroes = calloc(sizeof(proc_list_t), sizeof(uint8_t));
	if (NULL == zeroes) {
		LOG(L_ERROR,("calloc(%#x,%#x) call failed (%s)\n",
			(unsigned)sizeof(proc_list_t),
			(unsigned)sizeof(uint8_t),
			strerror(errno)));
		rc = -1;
		goto bailout;
	}
	write(pid_fd, zeroes, sizeof(proc_list_t));
	close(pid_fd);
	free(zeroes);
	zeroes = NULL;

	pid_fd = open(filename, O_RDWR|O_BINARY);
	if (-1 == pid_fd) {
		LOG(L_ERROR,("open('%s', O_RDWR|O_BINARY) call failed (%s)\n",
			filename, strerror(errno)));
		rc = -1;
		goto bailout;
	}

	/* allocate an array of pointers to proc info */
	flags = MAP_SHARED;
#ifdef	MAP_NOSYNC
	flags |= MAP_NOSYNC;
#endif
#ifdef	MAP_HASSEMAPHORE
	flags |= MAP_HASSEMAPHORE;
#endif
	g_proc = (proc_list_t *) mmap(NULL, sizeof(proc_list_t),
		PROT_READ | PROT_WRITE, flags, pid_fd, 0);
	if (MAP_FAILED == (caddr_t)g_proc || NULL == g_proc) {
		LOG(L_ERROR,("mmap(NULL, %d, PROT_READ|PROT_WRITE, %d, %d, %d) call failed (%s)\n",
			(int)sizeof(proc_list_t), flags, pid_fd, 0, strerror(errno)));
		rc = -1;
		goto bailout;
	}
	if (0 != (rc = osd_sem_init(&g_proc->sem, 1, 1))) {
		LOG(L_ERROR,("osd_sem_init(%p) call failed (%s)\n",
			&g_proc->sem, strerror(errno)));
		rc = -1;
		goto bailout;
	}

bailout:
	if (0 == rc) {
		switch (osd_fork2("proc_init", g_conf->niceness, -1)) {
		case -1:
			LOG(L_NORMAL,("osd_fork2() call failed (%s)\n",
				strerror(errno)));
			break;
		case 0:
			if (setsid() < 0) {
				LOG(L_ERROR,("setsid() call failed (%s)\n", strerror(errno)));
				g_proc->pgrp = (pid_t)-1;
				osd_exit(-1);
			}
			g_proc->pgrp = getpgrp();
			seconds = 0;
			for (;;) {
				if ((pid_t)-1 == g_proc->pgrp)
					break;
				osd_sleep(1);
				if (++seconds >= 300) {
					LOG(L_NORMAL,("--- %s main proc sleeping ---\n",
						g_conf->progname));
					seconds -= 300;
				}
			}
			osd_exit(rc);
		}
	}
	if (-1 != pid_fd) {
		close(pid_fd);
		pid_fd = -1;
	}
	if (NULL != zeroes) {
		free(zeroes);
		zeroes = NULL;
	}
	if (NULL != filename) {
		free(filename);
		filename = NULL;
	}
	if (MAP_FAILED != (caddr_t)g_proc || NULL != g_proc) {
		/* wait for child to init g_proc->pgrp */
		while (0 == g_proc->pgrp) {
			osd_usleep(1000);
		}
#if	USE_POSIX_SEMAPHORES
		info("POSIX sem; PGID %d ", g_proc->pgrp);
#elif USE_SVID_SEMAPHORES
		info("SVID sem; PGID %d ", g_proc->pgrp);
#elif USE_FLOCK_SEMAPHORES
		info("FLOCK sem; PGID %d ", g_proc->pgrp);
#else
		info("ASM sem; PGID %d ", g_proc->pgrp);
#endif
	} else {
		info("failed ");
	}
	return rc;
}

#else	/* ! HAVE_MMAP */

int proc_init(int argc, char **argv)
{
	int flags, seconds, fd, rc = 0;
	FUN("proc_init");

	(void)argc;

	proc_key = ftok(argv[0], 0);
	if (-1 == proc_key) {
		LOG(L_ERROR,("ftok('%s', %d) call failed (%s)\n",
			g_conf->program, 0, strerror(errno)));
		rc = -1;
		goto bailout;
	}
	flags = IPC_CREAT | SHM_R | SHM_W | (SHM_R >> 3) | (SHM_W) >> 3;
	proc_shmid = shmget(proc_key, sizeof(proc_list_t), flags);
	if (-1 == proc_shmid) {
		LOG(L_ERROR,("shmget(%#x, %d, 0%o) call failed (%s)\n",
			proc_key, sizeof(proc_list_t), flags, strerror(errno)));
		rc = -1;
		goto bailout;
	}
	g_proc = (proc_list_t *)shmat(proc_shmid, NULL, 0);
	if (-1 == (int)g_proc) {
		LOG(L_ERROR,("shmat(%#x, NULL, 0) call failed (%s)\n",
			proc_shmid, strerror(errno)));
		g_proc = NULL;
		rc = -1;
		goto bailout;
	}
	memset(g_proc, 0, sizeof(proc_list_t));

	if (0 != (rc = osd_sem_init(&g_proc->sem, 1, 1))) {
		LOG(L_ERROR,("osd_sem_init(%p) call failed (%s)\n",
			&g_proc->sem, strerror(errno)));
		rc = -1;
		goto bailout;
	}
bailout:
	if (0 == rc) {
		switch (osd_fork2("proc_init", g_conf->niceness, -1)) {
		case -1:
			LOG(L_NORMAL,("osd_fork2() call failed (%s)\n",
				strerror(errno)));
			break;
		case 0:
			if (setsid() < 0) {
				LOG(L_ERROR,("setsid() call failed (%s)\n", strerror(errno)));
				g_proc->pgrp = (pid_t)-1;
				osd_exit(-1);
			}
			g_proc->pgrp = getpgrp();
			seconds = 0;
			for (;;) {
				if ((pid_t)-1 == g_proc->pgrp)
					break;
				osd_sleep(1);
				if (++seconds >= 300) {
					LOG(L_NORMAL,("--- %s main proc sleeping ---\n",
						g_conf->progname));
					seconds -= 300;
				}
			}
			osd_exit(rc);
		}
	}
	if (-1 != (int)g_proc) {
		/* wait for child to init g_proc->pgrp */
		while (0 == g_proc->pgrp) {
			osd_usleep(1000);
		}
#if	USE_POSIX_SEMAPHORES
		info("POSIX sem; PGID %d ", g_proc->pgrp);
#elif USE_SVID_SEMAPHORES
		info("SVID sem; PGID %d ", g_proc->pgrp);
#elif USE_FLOCK_SEMAPHORES
		info("FLOCK sem; PGID %d ", g_proc->pgrp);
#else
		info("ASM sem; PGID %d ", g_proc->pgrp);
#endif
	} else {
		info("failed ");
	}
	return rc;
}
#endif

int proc_exit(void)
{
	char *filename = calloc(MAXPATHLEN, 1);
	pid_t pgrp = 0;
	int i, flags, fd = -1, rc = 0;
	FUN("proc_exit");

	if (NULL == filename) {
		LOG(L_ERROR,("calloc(%d,%d) call failed (%s)\n",
			MAXPATHLEN, 1, strerror(errno)));
		return -1;
	}

	pm_snprintf(filename, MAXPATHLEN, "%s%s.pid",
		g_conf->runpath, g_conf->progname);
	fd = open(filename, O_RDWR|O_BINARY);
	if (-1 == fd) {
		LOG(L_ERROR,("open('%s', O_RDWR|O_BINARY) call failed (%s)\n",
			filename, strerror(errno)));
		return -1;
	}
	info("Found PID file: %s\n", filename);

#if	HAVE_MMAP
	/* allocate an array of pointers to proc info */
	flags = MAP_SHARED;
	g_proc = (proc_list_t *) mmap(NULL, sizeof(proc_list_t),
		PROT_READ | PROT_WRITE, flags, fd, 0);
	if (MAP_FAILED == (caddr_t)g_proc || NULL == g_proc) {
		LOG(L_ERROR,("mmap(NULL, %d, PROT_READ|PROT_WRITE, %d, %d, %d) call failed (%s)\n",
			(int)sizeof(proc_list_t), flags, fd, 0, strerror(errno)));
		rc = -1;
		goto bailout;
	}
#else
	proc_key = ftok(g_conf->program, 0);
	if (-1 == proc_key) {
		LOG(L_ERROR,("ftok('%s', %d) call failed (%s)\n",
			g_conf->program, 0, strerror(errno)));
		rc = -1;
		goto bailout;
	}
	flags = IPC_CREAT | SHM_R | SHM_W | (SHM_R >> 3) | (SHM_W >> 3);
	proc_shmid = shmget(proc_key, sizeof(proc_list_t), flags);
	if (-1 == proc_shmid) {
		LOG(L_ERROR,("shmget(%#x, %d, 0%o) call failed (%s)\n",
			proc_key, sizeof(proc_list_t), flags, strerror(errno)));
		rc = -1;
		goto bailout;
	}
	g_proc = (proc_list_t *)shmat(proc_shmid, NULL, 0);
	if (-1 == (int)g_proc) {
		LOG(L_ERROR,("shmat(%#x, NULL, 0) call failed (%s)\n",
			proc_shmid, strerror(errno)));
		g_proc = NULL;
		rc = -1;
		goto bailout;
	}
#endif

	/* lock the process list semaphore */
	osd_sem_wait(&g_proc->sem);

	pgrp = g_proc->pgrp;
	/* indicate shutdown */
	g_proc->pgrp = (pid_t)-1;

	info("Killing %s processes:\n", g_conf->progname);
	/* first round: kill the processes with SIGHUP */
	for (i = MAXPROC - 1; i >= 0; i--) {
		proc_t *p = &g_proc->entry[i];
		if (0 != p->pid) {
			if (0 == kill(p->pid, 0)) {
				info("%-7d %s: ", p->pid, p->name);
				if (0 == kill(p->pid, SIGHUP)) {
					info("SIGHUP ok\n");
				}
			} else {
				LOG(L_ERROR,("proc {%d} '%s' is dead (%s)\n",
					(int)p->pid, p->name, strerror(errno)));
				p->pid = 0;
			}
		}
	}

	osd_sleep(1);
	/* second round through; now kill the remaining processes with SIGKILL */
	for (i = MAXPROC - 1; i >= 0; i--) {
		proc_t *p = &g_proc->entry[i];
		if (0 != p->pid) {
			if (0 == kill(p->pid, 0)) {
				info("%-7d %s: ", p->pid, p->name);
				if (0 == kill(p->pid, SIGKILL)) {
					info("SIGKILL ok\n");
				} else {
					info("FAILED\n");
				}
			}
			memset(p, 0, sizeof(*p));
		}
	}

bailout:
	/* shutdown done */
	g_proc->pgrp = 0;
	osd_sem_post(&g_proc->sem);

#if	HAVE_MMAP
	if (MAP_FAILED != (caddr_t)g_proc && NULL != g_proc) {
		munmap((caddr_t)g_proc, sizeof(proc_list_t));
		g_proc = NULL;
	}
#else
	if (-1 != (int)g_proc && NULL != g_proc) {
		shmdt(g_proc);
		g_proc = NULL;
	}
	if (-1 != proc_shmid) {
		shmctl(proc_shmid, IPC_RMID, 0);
		proc_shmid = -1;
	}
	proc_key = -1;
#endif
	if (-1 != fd) {
		close(fd);
		fd = -1;
	}
	if (NULL != filename) {
		/* remove the .pid file now */
		LOG(L_MINOR,("unlink(%s) process ID file\n",
			filename));
		unlink(filename);
		free(filename);
		filename = NULL;
	}
	if (0 != pgrp) {
		/* kill process group!? */
		kill(-pgrp, SIGHUP);
		/* kill process leader */
		kill(pgrp, SIGHUP);
	}

	return rc;
}

int proc_add(pid_t pid, const char *name)
{
	pid_t old_pid;
	char old_name[120];
	proc_t *p = NULL;
	int i;
	FUN("proc_add");

	LOG(L_MINOR,("pid: %d, name: '%s'\n", (int)pid, name));
	if (0 != osd_sem_wait(&g_proc->sem)) {
		LOG(L_ERROR,("locking g_proc failed (%s)\n",
			strerror(errno)));
		return -1;
	}
	for (i = 0; i < MAXPROC; i++) {
		p = &g_proc->entry[i];
		old_pid = p->pid;
		strncpy(old_name, p->name, sizeof(old_name));
		old_name[sizeof(old_name)-1] = '\0';
		if (0 == old_pid) {
			old_name[0] = '\0';
			break;
		}
		/* now kill(0) the process to see if it is still alive */
		if (0 != kill(old_pid, 0)) {
			LOG(L_ERROR,("proc {%d} '%s' is dead (%s)\n",
				(int)old_pid, old_name, strerror(errno)));
			memset(p, 0, sizeof(*p));
			break;
		}
	}
	if (i >= MAXPROC) {
		osd_sem_post(&g_proc->sem);
		errno = ENOMEM;
		return -1;
	}
	p->pid = pid;
	p->time = time(NULL);
	strncpy(p->name, name, sizeof(p->name));
	p->name[sizeof(p->name) - 1] = '\0';
	osd_sem_post(&g_proc->sem);
	return 0;
}

int proc_del(pid_t pid)
{
	int i;
	FUN("proc_del");

	LOG(L_MINOR,("pid: %d\n", (int)pid));

	for (i = 0; i < MAXPROC; i++) {
		proc_t *p = &g_proc->entry[i];
		if (pid == p->pid) {
			memset(p, 0, sizeof(*p));
			return 0;
		}
	}
	errno = ENOENT;
	return -1;
}

char *proc_name(pid_t pid)
{
	int i;
	FUN("proc_name");

	if (0 != osd_sem_wait(&g_proc->sem)) {
		LOG(L_ERROR,("locking g_proc failed (%s)\n",
			strerror(errno)));
		return "<lock error>";
	}
	for (i = 0; i < MAXPROC; i++) {
		proc_t *p = &g_proc->entry[i];
		if (pid == p->pid) {
			osd_sem_post(&g_proc->sem);
			return p->name;
		}
	}
	osd_sem_post(&g_proc->sem);
	return "<unknown>";
}

int proc_info(int n, proc_t *dst)
{
	proc_t *p = &g_proc->entry[n];
	FUN("proc_info");
	if (n < 0 || n >= MAXPROC) {
		LOG(L_ERROR,("invalid proc number %d\n", n));
		errno = EINVAL;
		return -1;
	}
	if (0 != kill(p->pid, 0)) {
		LOG(L_ERROR,("proc {%d} '%s' is dead (%s)\n",
			(int)p->pid, p->name, strerror(errno)));
		memset(p, 0, sizeof(*p));
	}
	memcpy(dst, p, sizeof(*dst));
	return 0;
}

const char *signal_name(int sig)
{
#if	HAVE_DECL_SYS_SIGNAME
	return sys_signame[sig];
#elif	HAVE_DECL__SYS_SIGNAME
	return _sys_signame[sig];
#else
	switch (sig) {
	/* traditional guessed defaults */
	case 0: return "0";
	case 1: return "SIGHUP";
	case 2: return "SIGINT";
	case 3: return "SIGQUIT";
	case 4: return "SIGILL";
	case 5: return "SIGTRAP";
	case 6: return "SIGABRT";
	case 7: return "SIGEMT";
	case 8: return "SIGFPE";
	case 9: return "SIGKILL";
	case 10: return "SIGBUS";
	case 11: return "SIGSEGV";
	case 12: return "SIGSYS";
	case 13: return "SIGPIPE";
	case 14: return "SIGALRM";
	case 15: return "SIGTERM";
	case 16: return "SIGURG";
	case 17: return "SIGSTOP";
	case 18: return "SIGTSTP";
	case 19: return "SIGCONT";
	case 20: return "SIGCHLD";
	case 21: return "SIGTTIN";
	case 22: return "SIGTTOU";
	case 23: return "SIGIO";
	case 24: return "SIGXCPU";
	case 25: return "SIGXFSZ";
	case 26: return "SIGVTALRM";
	case 27: return "SIGPROF";
	case 28: return "SIGWINCH";
	case 29: return "SIGINFO";
	case 30: return "SIGUSR1";
	case 31: return "SIGUSR2";
	default: return "SIG???";
	}
#endif
}
